package com.xlabs.server;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;

import javax.jdo.PersistenceManager;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.xlabs.client.CommentingService;
import com.xlabs.client.DTO.QuestionDTO;
import com.xlabs.client.DTO.ReplyDTO;
import com.xlabs.client.DTO.TransferDTO;
import com.xlabs.server.DO.PMF;
import com.xlabs.server.DO.QueryResult;
import com.xlabs.server.DO.Question;
import com.xlabs.server.DO.Reply;
import com.xlabs.server.DO.Video;


/**
 * TimedCommentServiceImpl class.
 */
public class CommentingServiceImpl extends RemoteServiceServlet implements CommentingService 
{

    /**
	 * 
	 */
	private static final long serialVersionUID = 1714415619178522139L;

	/**
     * Posts comment for a video.
     *
     * @param comment        comment text.
     * @param time           time of comment.
     * @param currentVideoId video id.
     */
    public void postQuestion(String comment, int time, String currentVideoId) 
    {
        Video.addQuestionToVideo(comment, time, currentVideoId);
    }
    
    /**
     * see {@link CommentingService}{@link #getPagedQuestionsAroundTime(String, int, int)}
     */
    public TransferDTO getPagedQuestionsWithStartTimeAroundTime(String videoId, int time, int pageSize)
	{
    	PersistenceManager pm = PMF.get().getPersistenceManager();
    	
    	try
    	{
	    	Video.AddVideo(videoId);
	    	
			List<Question> rightQuestions = ((QueryResult<List<Question>>) Question.queryQuestionsByStartTimeIndex(
												String.valueOf(time), 
												String.valueOf(Integer.MAX_VALUE), 
												TransferDTO.RIGHT_DIRECTION,
												pageSize + 1, pm)).getResults();

			List<Question> leftQuestions = ((QueryResult<List<Question>>) Question.queryQuestionsByStartTimeIndex(
												String.valueOf(Integer.MIN_VALUE), 
												String.valueOf(time), 
												TransferDTO.LEFT_DIRECTION,
												pageSize + 1, pm)).getResults();

			TransferDTO response = new TransferDTO();
			List<QuestionDTO> dtoList = new ArrayList<QuestionDTO>();
			Boolean hasNext = null;
			Boolean hasPrev = null;
			
			if (rightQuestions.size() >= pageSize)
			{
				dtoList = Question.toDTOList(rightQuestions.subList(0, Math.min(rightQuestions.size(), pageSize)));
				hasNext = (rightQuestions.size() == pageSize + 1);
				hasPrev = (leftQuestions.size() >= 1);
			}
			else
			{
				List<Question> mergedQuestions = new ArrayList<Question>();
				int questionsToAdd = pageSize - rightQuestions.size();
				mergedQuestions = leftQuestions.subList(0, Math.min(questionsToAdd,	leftQuestions.size()));
				int questionsAddedFromLeft = mergedQuestions.size();
	
				Collections.reverse(mergedQuestions);
	
				List<Question> questionsToAddFromRight = rightQuestions.subList(
																			0, 
																			Math.min(
																					rightQuestions.size(), 
																					pageSize - mergedQuestions.size()
																					)
																				 );
	
				mergedQuestions.addAll(questionsToAddFromRight);
	
				dtoList = Question.toDTOList(mergedQuestions);
				hasNext = false;
				hasPrev = (leftQuestions.size() > questionsAddedFromLeft); 
			}
			
	    	response.addKeyValue(TransferDTO.RESULT_KEY, dtoList);
	    	response.addKeyValue(TransferDTO.HAS_NEXT_QUESTIONPAGE_KEY, hasNext);
	    	response.addKeyValue(TransferDTO.HAS_PREV_QUESTIONPAGE_KEY, hasPrev);
			response.addKeyValue(TransferDTO.NEXT_PAGE_QUESTION_START_TIME_INDEX_KEY, hasNext ? rightQuestions.get(pageSize).getStartTimeIndex() : null);
	    	
	    	return response;
    	}
    	finally
    	{
    		pm.close();
    	}
	}
    
    /**
     * Posts a reply for a question.
     *
     * @param replyText             reply.
     * @param questionStringKey question key.
     */
    public void postReply(String replyText, String questionStringKey) 
    {
        PersistenceManager pm = PMF.get().getPersistenceManager();

        try 
        {
            UserService userService = UserServiceFactory.getUserService();
            Key questionKey = KeyFactory.stringToKey(questionStringKey);
            Question questionDO = pm.getObjectById(Question.class, questionKey);

            Reply reply = new Reply(replyText, new Date(), userService.getCurrentUser());

            reply.setReplyTo(questionKey);
            reply.setOriginalQuestion(questionKey);
            pm.makePersistent(reply);
            questionDO.addReply(reply);
        }
        finally 
        {
            pm.close();
        }
    }

    /**
     * Posts an inner reply for another reply in conversation.
     *
     * @param reply          reply.
     * @param parentReplyEncodedKey question key.
     * @param questionEncodedKey corresponding question's encoded key
     */
    public void posInnertReply(String reply, String parentReplyEncodedKey, String questionEncodedKey) 
    {

        Key parentReplyKey = KeyFactory.stringToKey(parentReplyEncodedKey);
        Key questionKey = KeyFactory.stringToKey(questionEncodedKey);
        PersistenceManager pm = PMF.get().getPersistenceManager();

        try 
        {
            UserService userService = UserServiceFactory.getUserService();
            Reply parentReplyDO = pm.getObjectById(Reply.class, parentReplyKey);
            Reply replyDO = new Reply(reply, new Date(), userService.getCurrentUser());

            replyDO.setReplyTo(parentReplyKey);
            replyDO.setOriginalQuestion(questionKey);
            pm.makePersistent(replyDO);
            parentReplyDO.addReply(replyDO.getKey());
        }
        finally 
        {
            pm.close();
        }
    }

	/**
	 * Gets a question responses according to the given index and range. 
	 * @param questionEncodedKey Question key. 
	 * @param startIndexInc start Index.
	 * @param endIndexExcl end index. 
	 * @param direction query direction (left or right). 
	 * @param pageSize page size. 
	 * @param callback call back.
	 */
    public TransferDTO getQuestionResponsesByIndex(
			String questionEncodedKey, 
			String startIndexInc,
			String endIndexExcl, 
			int direction, 
			int pageSize)		
	{
    	Key questionKey = KeyFactory.stringToKey(questionEncodedKey);
    	PersistenceManager pm = PMF.get().getPersistenceManager();
    	
        try 
        {
        	QueryResult<List<Reply>> queryResult = Reply.queryResponsesByCreatedOnIndex(
									        			questionKey,
									        			startIndexInc, 
									        			endIndexExcl,
									        			direction,
									        			pageSize + 1,
									        			pm);
        	
			return addPagingFlags(endIndexExcl, direction, pageSize, queryResult);
        }
        finally
        {
            pm.close();
        }
	}

	/**
	 * Adds paging flags to the response transfer object.
	 * @param endIndexExcl End index excluding.
	 * @param direction direction of query.
	 * @param pageSize size of page returned.
	 * @param queryResult result of query.
	 * @return Transfer DTO with paging flags.
	 */
    private TransferDTO addPagingFlags(
			String endIndexExcl, 
			int direction,
			int pageSize, 
			QueryResult<List<Reply>> queryResult)
	{
		boolean fullPage = (queryResult.getResults().size() == pageSize + 1);
		List<ReplyDTO> dtoList = Reply.toDTOList(queryResult.getResults());
		TransferDTO response = new TransferDTO();

		if (direction == TransferDTO.LEFT_DIRECTION)
		{
			Collections.reverse(dtoList);
			response.addKeyValue(TransferDTO.HAS_PREV_QUESTIONPAGE_KEY, fullPage);
			response.addKeyValue(TransferDTO.NEXT_PAGE_QUESTION_START_TIME_INDEX_KEY, endIndexExcl);

			if (fullPage)
			{
				dtoList.remove(0);
			}
		}
		else if (direction == TransferDTO.RIGHT_DIRECTION)
		{
			response.addKeyValue(TransferDTO.HAS_NEXT_QUESTIONPAGE_KEY, fullPage);
			response.addKeyValue(TransferDTO.NEXT_PAGE_QUESTION_START_TIME_INDEX_KEY, fullPage ? queryResult.getResults().get(pageSize).getCreatedOnIndex() : null);
			
			if (fullPage)
			{
				dtoList.remove(pageSize);
			}
		}

		response.addKeyValue(TransferDTO.RESULT_KEY, dtoList);
		
		return response;
	}

	/**
	 * Gets a response's responses according to the given index and range. 
	 * @param parentReplyEncodedKey response key. 
	 * @param startIndexInc start Index.
	 * @param endIndexExcl end index. 
	 * @param direction query direction (left or right). 
	 * @param pageSize page size. 
	 */
    public TransferDTO getInnerRepliesByIndex(
			String parentReplyEncodedKey, 
			String startIndexInc,
			String endIndexExcl, 
			int direction, 
			int pageSize)
    {
    	Key parentReplyKey = KeyFactory.stringToKey(parentReplyEncodedKey);
    	PersistenceManager pm = PMF.get().getPersistenceManager();
    	
        try 
        {
        	QueryResult<List<Reply>> queryResult = Reply.queryResponsesByCreatedOnIndex(
									        			parentReplyKey,
									        			startIndexInc, 
									        			endIndexExcl,
									        			direction,
									        			pageSize + 1,
									        			pm);
        	
			return addPagingFlags(endIndexExcl, direction, pageSize, queryResult);
        }
        finally
        {
            pm.close();
        }
    }

	/**
     * Sets a reply as an answer or nonanswer to a question. 
     * @param questionEncodedKey The question encoded key.
     * @param replyEncodedKey The relpy encoded key.
     * @param isAnswer whether to set the reply as an answer (or non-answer).
	 * @return The result of the operation.
	 */
    public TransferDTO setReplyAsAnswer(
			String questionEncodedKey, 
			String replyEncodedKey,
			boolean isAnswer)
	{
    	Key questionKey = KeyFactory.stringToKey(questionEncodedKey);
    	Key replyKey = KeyFactory.stringToKey(replyEncodedKey);
    	
    	PersistenceManager pm = PMF.get().getPersistenceManager();
    	try
    	{
    		Question questionDO = pm.getObjectById(Question.class, questionKey);
    		Reply replyDO = pm.getObjectById(Reply.class, replyKey);

    		Reply previousReplyDO = null;
    		if (questionDO.getAnswer() != null)
    		{
    			previousReplyDO = pm.getObjectById(Reply.class, questionDO.getAnswer());
    		}
    		
    		if (isAnswer)
    		{
	    		questionDO.setAnswer(replyKey);
	    		replyDO.setAnswerFor(questionKey);
	    		if (previousReplyDO != null)
	    		{
	    			previousReplyDO.setAnswerFor(null);
	    		}
    		}
    		else
    		{
    			questionDO.setAnswer(null);
    			replyDO.setAnswerFor(null);
    		}
    	}
    	finally
    	{
    		pm.close();
    	}
    	
		return new TransferDTO();
	}
}